/*
 *system init
 *
 *
 * 
 * 时钟源可配置为来自外部振荡器、石英晶体谐振器、陶瓷谐振器以及阻容(Resistor-Capacitor,RC)电路。
 * 此外,系统时钟源可配置为 3 个内部振荡器之一,并通过软件来选择速度。
 *
 * 其他时钟特性包括:
 * 可通过软件选择外部或内部系统时钟源。
 * 双速启动模式,最大限度地缩短外部振荡器起振与代码执行之间的延时。
 * 故障保护时钟监视器 (Fail-Safe Clock Monitor,FSCM),用来检测外部时钟源 (LP、 XT、 HS、EC 或 RC 模式)故障并自动切换到内部振荡器。
 * 振荡器起振定时器(Oscillator Start-up Timer,OST),可确保晶振源的稳定性。
 *
 *
 *主时钟模块可配置为 6 种时钟源,从中选择一种作为主时钟。
 *1.RC      外部电阻 / 电容
 *2.LP      低功耗晶振
 *3.XT      晶振 / 谐振器
 *4.INTOSC  内部振荡器
 *5.HS      高速晶振 / 谐振器
 *6.EC      外部时钟
 *
 * 震荡器
 * SOSC     辅助震荡器
 * OSC      主震荡器
 * HFINTOSC 内部高频震荡器16M
 * MFINTOSC 内部中频震荡器500K
 * LFINTOSC 内部低频震荡器31.25K
 *
 * LFINTOSC 的输出可用作主时钟或 INTOSC 时钟的时钟源。
 * LFINTOSC 还是上电延时定时器(PWRT)、看门狗定时器 (WDT)以及故障保护时钟监视器 (FSCM)的时钟源。
 *
 * 通过以下配置和寄存器位可以进一步定义主时钟工作模式:
 * 1.PRICLKEN (CONFIG1H<5>)
 * 2.PRISD (OSCCON2<2>)
 * 3.PLLCFG (CONFIG1H<4>)
 * 4.PLLEN (OSCTUNE<6>)
 * 5.HFOFST (CONFIG3H<3>)
 * 6.IRCF<2:0> (OSCCON<6:4>)
 * 7.MFIOSEL (OSCCON2<4>)
 * 8.INTSRC (OSCTUNE<7>)
 *
 * HFINTOSC、MFINTOSC 和 LFINTOSC 分别为出厂校准的高频、中频和低频振荡器,可用作内部时钟源。
 *
 * 时钟控制寄存器
 * OSCCON、OSCCON2 和 OSCTUNE
 *
 * 主系统时钟选择       (SCS)         用于时钟切换
 * 主振荡器电路关闭     (PRISD)
 * 辅助振荡器使能       (SOSCGO)
 * 主时钟频率 4x 倍频器 (PLLEN)
 * 内部频率选择位       (IRCF 和 INTSRC)
 * 时钟状态位          (OSTS、 HFIOFS、 MFIOFS、LFIOFS、 SOSCRUN 和 PLLRDY)
 * 功耗管理选择        (IDLEN)
 * 
 *当主振荡器为主时钟源时, OSTS 还特别指明在LP、 XT 或 HS 模式下,振荡器起振定时器 (OST)是否已超时。
 *
 * 

 *
 * 对于使用 OSC1/OSC2 引脚的任何外部振荡器模式,都可以使能PLL,
 * 方法是将PLLCFG位(CONFIG1H<4>)置 1,或将 PLLEN 位 (OSCTUNE<6>)置 1
 * PLL 只能通过软件进行控制。当使用 HFINTOSC 时,OSCTUNE 寄存器的 PLLEN 控制位用于使能或禁止 PLL 操作。
 *
 *当选取 PRI_IDLE 模式时,指定的主振荡器会继续运行而不中断。
 *对于所有其他功耗管理模式,使用 OSC1 引脚的振荡器会被禁止。OSC1 引脚(以及由振荡器使用的 OSC2 引脚)将会停止振荡。
 *


 *
 * 主系统时钟选择
 * 系统时钟选择位 SCS<1:0> 用于选择主时钟源。可用的时钟源有:
 * • 由 CONFIG1H 的 FOSC<3:0> 位定义的主时钟。主时钟可以是主振荡器、外部时钟或内部振荡器模块。
 * • 辅助时钟 (辅助振荡器)
 * • 内部振荡器模块 (HFINTOSC、 MFINTOSC 和LFINTOSC)
 * 当写入一个或多个位之后,在一段很短的时钟转换间隔后,时钟源会立即改变。在所有形式的复位后 SCS 位都会被清零以选择主时钟。
 * 如果振荡器模块配置为 LP、 XT 或 HS 模式,则振荡器起振定时器(OST)对来自 OSC1 的振荡计数 1024 次‘
 * 当使能 PLL 且选取外部振荡器模式时,器件将在 OST延时之后另外保持 2 ms 的复位状态,这样可使 PLL 锁定到输入时钟频率。
 *
 * 

 *
 * 在可使用 CONFIG1H 配置寄存器 FOSC<3:0> 位选择的时钟模式中,有两种模式会将内部振荡器模块配置为主振荡器。
 *

 * 双速启动模式配置
 * 当 CONFIG1H 配置寄存器的 IESO 置 1 时,使能双速启动模式。
 * SCS<1:0> (在 OSCCON 寄存器中) = 00。
 * CONFIG1H 配置寄存器的 FOSC<2:0> 位被配置为LP、 XT 或 HS 模式。
 *
 * 在以下事件之后,双速启动模式变为有效:
 * 上电复位(POR)以及在上电延时定时器(PWRT) 延时结束 (如果使能)后,或者从休眠中唤醒。
 * 故障保护检测
 * 当外部时钟出现故障时, FSCM 将器件时钟切换到内部时钟源,并将 PIR2 寄存器的 OSFIF 标志位置 1
 *
 *
 *
 *
 * 
 * 
 */

#include"../Driver_PIC/system.h"
#include"../Driver_PIC/systemconfig.h"
#define OSC_CLK               12000000UL          /* Oscillator frequency,the frequency must be larger than 4MHz */
#define OSCPLL_CLK            (4 * OSC_CLK)       /* Main oscillator frequency  */
#define SOSC_CLK                4000000UL
#define HFINTOSC_CLK           16000000UL          /* Internal HIGH frequency    */
#define MFINTOSC_CLK             500000UL          /* Internal MIDOM frequency   */
#define LFINTOSC_CLK              31250UL          /* Internal LOW frequency     */

/*时钟控制寄存器有OSCCON，OSCCON2，OSCTUNE，以及系统配置字CONFIG1H*/
/*以下为寄存器配置位设置，系统配置字CONFIG1H见pic18f2xk22systemconfig.h*/
#define OSCCON_IDLEN_VAL      1                  /*【7】执行 SLEEP 指令后器件进入空闲模式/休眠模式*/
#define OSCCON_IRCF_VAL       7                  /*【6-4】内部 RC 振荡器频率选择位*/
#define OSCCON_SCS_VAL        0                  /*【1-0】系统时钟选择位*/
/*其余OSCCON位为状态位，HFINTOSC稳定位、震荡器来源指示位*/
/*
 * 【3】   OSTS:振荡器起振延时状态位
 *         1 = 器件依靠 CONFIG1H 寄存器中 FOSC<3:0> 定义的时钟运行
 *         0 = 器件依靠内部振荡器 (HFINTOSC、 MFINTOSC 或 LFINTOSC)运行
 * 【2】  HFIOFS:HFINTOSC 频率稳定位
 *         1 = HFINTOSC 频率稳定
 *         0 = HFINTOSC 频率不稳定
 */


#define OSCCON2_MFIOSEL_VAL   0                   /*【4】MFINTOSC 选择位*/
#define OSCCON2_SOSCGO_VAL    0                   /*【3】SOSCGO辅助振荡器使能/关闭*/
#define OSCCON2_PRISD_VAL     1                   /*【2】主振荡器驱动电路关闭位*/
/*其余OSCCON2位为状态位，系统时钟来源指示位、SOSC状态、MFINTOSC稳定位、LFINTOSC稳定位*/
/*
 *[7] PLLRDY:PLL 运行状态位
 *      1 = 系统时钟来自 4xPLL
 *      0 = 系统时钟来自除 4xPLL 外的振荡器
 *[6] SOSCRUN:SOSC 运行状态位
 *      1 = 系统时钟来自辅助 SOSC
 *      0 = 系统时钟来自除 SOSC 外的振荡器
 *[5] 未实现
 *[1] MFIOFS:MFINTOSC 频率稳定位
 *      1 = MFINTOSC 稳定
 *      0 = MFINTOSC 不稳定
 *[0] LFIOFS:LFINTOSC 频率稳定位
 *      1 = LFINTOSC 稳定
 *      0 = LFINTOSC 不稳定
 */

#define OSCTUNE_INTSRC_VAL    0                  /*【7】内部振荡器低频时钟源选择位*/
#define OSCTUNE_PLLEN_VAL     0                  /* [6] 针对HFINTOSC启动4*PLL*/
#define OSCTUNE_TUN_VAL       0                  /*【5-0】频率调节位*/



/*----------------------------------------------------------------------------
  Check the register settings
 *----------------------------------------------------------------------------*/
#define CHECK_RANGE(val, min, max)                ((val < min) || (val > max))
#define CHECK_RSVD(val, mask)                     (val & mask)



#if (CHECK_RANGE((OSC_CLK), 4000000UL,12000000UL))
   #error "OSC_CLK: Invalid values of reserved bits!"
#endif

unsigned long SystemClock = OSCPLL_CLK;
void SystemInit (void)
{
    OSCCONbits.IDLEN = OSCCON_IDLEN_VAL;
    OSCCONbits.IRCF = OSCCON_IRCF_VAL;
    

    OSCCON2bits.MFIOSEL = OSCCON2_MFIOSEL_VAL;
    OSCCON2bits.SOSCGO = OSCCON2_SOSCGO_VAL;
    OSCCON2bits.PRISD = OSCCON2_PRISD_VAL;

    OSCTUNEbits.INTSRC = OSCTUNE_INTSRC_VAL;
    OSCTUNEbits.PLLEN = OSCTUNE_PLLEN_VAL;
    OSCTUNEbits.TUN = OSCTUNE_TUN_VAL;

    RCONbits.IPEN = 1;                          /*使能中断优先级*/
    WDTCONbits.SWDTEN = 0 ;                     /*使能看门狗，当配置字相关位置位时该位无效*/

    /*开中断，GIEH 位可以禁止包括低优先级中断源在内的所有中断源*/
    INTCONbits.GIEH = 1;                        /*使能高优先级中断*/
    INTCONbits.GIEL = 1;                        /*使能地优先级中断*/

    /*内部时钟时不启动PLL*/
    if(__PLLCFG__ == 0)
    {
        if( __FOSC__!=8 && __FOSC__!= 9)
        {
            if(OSC_CLK > 4000000UL)
            {
                OSCTUNEbits.PLLEN = 1;
                while( ! OSCCON2bits.PLLRDY)          //等待PLL锁定
                {
                    asm("nop");
                }
            }
        }
        else
        {
            if((OSCCONbits.IRCF == 7) | (OSCCONbits.IRCF == 6))
            {
                OSCTUNEbits.PLLEN = 1;
                while( ! OSCCON2bits.PLLRDY)          //等待PLL锁定
                {
                    asm("nop");
                }
            }
        }
    }
    else
    {
        while( ! OSCCON2bits.PLLRDY)          //等待PLL锁定
        {
            asm("nop");
        }
    }

    /*
     * 当 INTCON寄存器的 GIEH 位置 1 时,可以允许所有相应的 IPRx寄存器或 INTCONx 寄存器优先级位置 1 (高优先级)的中断。
     * 当清零时, GIEH 位可以禁止包括低优先级中断源在内的所有中断源。
     * INTCON 寄存器的 GIEL 位在清零时,只能禁止相应的优先级位清零 (低优先级)的中断。
     * 当置 1 时, GIEL 位可以在 GIEH 位也置 1 时允许低优先级中断源。
     */
    OSCCONbits.SCS = OSCCON_SCS_VAL;
    RCONbits.IPEN = 1 ;           //使能中断优先级
    RCONbits.SBOREN = 1;          //使能欠压复位
    INTCONbits.GIE_GIEH = 1 ;     //开高优先级中断
    INTCONbits.PEIE_GIEL = 1 ;    //开低优先级中断

    WPUB = 0x00;                  //关闭端口B弱上拉
}
/*系统时钟更新*/
unsigned char SystemClockUpdate (void)
{
    if(OSCCON2bits.PLLRDY)                                           //系统时钟是否来自PLL
    {
        if(OSCCONbits.SCS == 0 && __FOSC__ != 8 && __FOSC__ != 9)    //主时钟做系统时钟，FOSC选择外部时钟
        {
            SystemClock = OSCPLL_CLK;
            return 1 ;
        }
        else if (OSCCONbits.SCS1 ==0 && (__FOSC__ & 0x0E) == 8)      //主时钟做系统时钟，FOSC选择内部时钟
        {
            if(OSCCONbits.IRCF == 7)
            {
                SystemClock = 4*HFINTOSC_CLK;
                return 1 ;
            }
            else if (OSCCONbits.IRCF == 6 )                          //主时钟做系统时钟，FOSC选择内部时钟
            {
                SystemClock = 2*HFINTOSC_CLK;
                return 1 ;
            }
        }
        return 0 ;
    }
    else
    {
        if(OSCCONbits.SCS == 1)                                     //辅助震荡器做系统时钟
        {
            SystemClock = SOSC_CLK;
            return 1;
        }
        else if (OSCCONbits.SCS == 0)                              //主时钟做系统时钟
        {
            if(__FOSC__ != 8 && __FOSC__ != 9)
            {
                SystemClock = OSC_CLK;
                return 1;
            }
            else
            {
                switch(OSCCONbits.IRCF)
                {
                    case 7 : SystemClock = HFINTOSC_CLK;      return 1;
                    case 6 : SystemClock = HFINTOSC_CLK / 2;  return 1;
                    case 5 : SystemClock = HFINTOSC_CLK / 4;  return 1;
                    case 4 : SystemClock = HFINTOSC_CLK / 8;  return 1;
                    case 3 : SystemClock = HFINTOSC_CLK / 16; return 1;
                    case 2 : SystemClock = 500000UL;          return 1;
                    case 1 : SystemClock = 250000UL;          return 1;
                    case 0 : SystemClock = 31250UL ;          return 1;
                    default: break;
                }
            }

        }
        else if (OSCCONbits.SCS1 == 1)                           //内部时钟做系统时钟
        {
            switch(OSCCONbits.IRCF)
            {
                case 7 : SystemClock = HFINTOSC_CLK;      return 1;
                case 6 : SystemClock = HFINTOSC_CLK / 2;  return 1;
                case 5 : SystemClock = HFINTOSC_CLK / 4;  return 1;
                case 4 : SystemClock = HFINTOSC_CLK / 8;  return 1;
                case 3 : SystemClock = HFINTOSC_CLK / 16; return 1;
                case 2 : SystemClock = 500000UL;          return 1;
                case 1 : SystemClock = 250000UL;          return 1;
                case 0 : SystemClock = 31250UL ;          return 1;
                default: break;          
            }            
        }
    }
    return 0;
}